﻿using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.ComponentModel;
using System.Reflection;

namespace BlazorAdmin.Web.Shared.Filter
{
    /// <summary>
    /// 检举文档描述过滤器
    /// </summary>
    public class SwaggerEnumFilter : IDocumentFilter
    {
        /// <summary>
        /// 程序集缓存
        /// </summary>
        public static List<Assembly> AssemblyCache { get; set; } = new List<Assembly>();

        /// <summary>
        /// 应用配置
        /// </summary>
        /// <param name="swaggerDoc">描述文档</param>
        /// <param name="context">上下文</param>
        public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        {
            Dictionary<string, Type>? enumCache = GetAll();
            foreach (var item in swaggerDoc.Components.Schemas)
            {
                var property = item.Value;
                var typeName = item.Key;
                Type itemType;
                if (null == property.Enum || property.Enum.Count < 1 || null == enumCache || !enumCache.ContainsKey(typeName))
                    continue;
                itemType = enumCache[typeName];

                var oaiList = (from e in property.Enum select (OpenApiInteger)e).ToList();
                property.Description += DescribeEnum(itemType, oaiList);
            }
        }

        private static string DescribeEnum(Type itemType, List<OpenApiInteger> dataList)
        {
            List<string> descList = new();
            foreach (var item in dataList)
            {
                var value = Enum.Parse(itemType, item.Value.ToString());
                var desc = GetDescription(itemType, value);
                if (string.IsNullOrEmpty(desc))
                    descList.Add($"{item.Value}:{Enum.GetName(itemType, value)};");
                else
                    descList.Add($"{item.Value}:{Enum.GetName(itemType, value)},{desc};");
            }
            return $"<br/>{string.Join("<br/>", descList)}";
        }
        private static string GetDescription(Type type, object value)
        {
            string? name = type.GetEnumName(value);
            foreach (var item in type.GetMembers())
            {
                if (item.Name == name)
                {
                    foreach (Attribute attr in Attribute.GetCustomAttributes(item))
                    {
                        if (attr is DescriptionAttribute da)
                            return da.Description;
                    }
                }
            }
            return string.Empty;
        }

        private static Dictionary<string, Type>? GetAll()
        {
            if (AssemblyCache.Count < 1)
                return default;

            Dictionary<string, Type> typeCache = new();
            foreach (var assembly in AssemblyCache)
            {
                var types = assembly.GetTypes();
                types = (from t in types where t.IsEnum select t).ToArray();
                if (null == types || types.Length < 1)
                    continue;

                foreach (var t in types)
                {
                    if (!typeCache.ContainsKey(t.Name))
                        typeCache.Add(t.Name, t);
                }
            }
            return typeCache;
        }
    }
}
